// Memory Buffer
// (c) jimon game studio

#ifndef JEH_JEMEMORYBUFFER
#define JEH_JEMEMORYBUFFER

#include "IBuffer.h"
#include <memory.h>

namespace je
{
	namespace core
	{
		//! Memory Buffer
		class jeMemoryBuffer:public je::core::IBuffer
		{
		private:
			//! Array
			c8 * Array;

			//! Size
			s32 Size;

			//! Lock Mode
			BufferLockType LockMode;

			//! True If Locked
			u1 Locked;

			//! Position
			u32 Position;
		public:
			//! Constructor
			jeMemoryBuffer()
				:Array(NULL),Size(0),Locked(false),Position(0)
			{
			}

			//! Constructor
			jeMemoryBuffer(const void * Pointer,const u32 SizeInBytes)
				:Array(NULL),Size(0),Locked(false),Position(0)
			{
				Lock(BLT_WRITE);
				Allocate(SizeInBytes);
				Write(Pointer,SizeInBytes);
				UnLock();
			}

			//! Destructor
			~jeMemoryBuffer()
			{
				if(Array)
				{
					Lock(BLT_WRITE);
					DeAllocate();
					UnLock();
				}
			}

			//! Return Array Ptr
			void * GetPtr()
			{
				JEASSERT(Locked)
				return Array;
			}

			//! Lock Buffer
			void Lock(BufferLockType LockType)
			{
				JEASSERT(!Locked)

				Locked = true;
				LockMode = LockType;
			}

			//! UnLock Buffer
			void UnLock()
			{
				Locked = false;
			}

			//! Return True if locked
			u1 IsLocked()
			{
				return Locked;
			}

			//! Read Data
			void Read(void * Pointer,u32 Bytes)
			{
				JEASSERT(Locked)
				JEASSERT((LockMode == BLT_READ) || (LockMode == BLT_READWRITE))

				memcpy(Pointer,Array + Position,Bytes);

				if(StreamWorkMode)
					Position += Bytes;
			}

			//! Write Data
			void Write(const void * Pointer,u32 Bytes)
			{
				JEASSERT(Locked)
				JEASSERT((LockMode == BLT_WRITE) || (LockMode == BLT_READWRITE))

				JEASSERT((s32)(Position + Bytes) <= Size)

				memcpy(Array + Position,Pointer,Bytes);

				if(StreamWorkMode)
					Position += Bytes;
			}

			//! Write Data From Other Buffer
			void WriteFrom(IBuffer * Buffer,u32 Bytes)
			{
				Buffer->Read(Array + Position,Bytes);
			}

			//! Allocate Buffer for data,size is count of objects
			void Allocate(u32 NewSize)
			{
				JEASSERT(Locked)
				JEASSERT((LockMode == BLT_WRITE) || (LockMode == BLT_READWRITE))

				if(Array)
					Array = reinterpret_cast<c8*>(core::Realloc(Array,NewSize));
				else
					Array = reinterpret_cast<c8*>(core::Alloc(NewSize));

				Size = NewSize;
			}

			//! DeAllocate Buffer
			void DeAllocate()
			{
				JEASSERT(Locked)
				JEASSERT((LockMode == BLT_WRITE) || (LockMode == BLT_READWRITE))
				JEASSERT(Array)

				core::Free(Array);
				Array = NULL;
				Size = 0;
			}

			//! Set Position
			void SetPosition(u32 NewPosition,u1 Relative = false)
			{
				if(Relative)
					Position += NewPosition;
				else
					Position = NewPosition;
			}

			//! Get Position
			u32 GetPosition()
			{
				return Position;
			}

			//! Get Size in bytes
			u32 GetSize()
			{
				return Size;
			}

			//! Get Size of one object
			u32 GetObjectSize()
			{
				return sizeof(c8);
			}
		};
	}
}

#endif
